package parser;

import antlr.RecognitionException;
import java.util.List;

abstract class Expr {

    abstract void write(ByteStream out) throws RecognitionException;

    abstract void delete(ByteStream out) throws RecognitionException;

    abstract void writeLHS(ByteStream out) throws RecognitionException;

    public static Expr NONE = new None();
    public static Expr TRUE = new Bool(true);
    public static Expr FALSE = new Bool(false);

    void tuple_parameter(ByteStream out) throws RecognitionException {
        write(out);
        out.write(Opcodes.SEQUENCE_TO_LIST_OR_TUPLE);
        out.write(Opcodes.AS_TUPLE);
    }

    Expr negate() {
        return new Unary("neg", this);
    }

    Stmt assign(Expr rhs) throws RecognitionException {
        lhs();
        return new AsgnStmt(this, rhs);
    }

    void lhs() throws RecognitionException {
    }

    void writeAugAsgn(int op, Expr right, ByteStream out) throws RecognitionException {
        write(out);  // r x
        right.write(out); // r
        out.write(Opcodes.INPLACE);
        out.write(op);   // r+x
        writeLHS(out);   //
    }

    Patch branch(boolean onTrue, ByteStream out) throws RecognitionException {
        write(out);
        return out.branch(onTrue ? Opcodes.ON_TRUE : Opcodes.ON_FALSE);
    }

    public Stmt toStm() {
        return new Stmt() {
            void write(ByteStream out) throws RecognitionException {
                out.line(line);
                Expr.this.write(out);
                out.write(Opcodes.DROP);
            }
        };
    }

    private static class None extends Expr {

        void write(ByteStream out) throws RecognitionException {
            out.write(Opcodes.NONE);
        }

        void writeLHS(ByteStream out) throws RecognitionException {
            // Shouldn't get here protected by lhs()?
            throw new RecognitionException("Cannot assign to None");
        }

        void delete(ByteStream out) throws RecognitionException {
            // Shouldn't get here protected by lhs()?
            throw new RecognitionException("Cannot delete None");
        }

        void lhs() throws RecognitionException {
            throw new RecognitionException("Cannot assign to None");
        }

    }

    private static class Bool extends Expr {

        private boolean value;
        private String name;

        Bool(boolean b) {
            this.value = b;
            this.name = b ? "True" : "False" ;
        }

        void write(ByteStream out) throws RecognitionException {
            out.write(value ? Opcodes.TRUE : Opcodes.FALSE);
        }

        void writeLHS(ByteStream out) throws RecognitionException {
            // Shouldn't get here protected by lhs()?
            throw new RecognitionException("Cannot assign to " + name);
        }

        void delete(ByteStream out) throws RecognitionException {
            // Shouldn't get here protected by lhs()?
            throw new RecognitionException("Cannot delete " + name);
        }

        void lhs() throws RecognitionException {
            throw new RecognitionException("Cannot assign to " + name);
        }

    }

}
